#include "mainwindow.h"
#include "ui_mainwindow.h"

#define CONFIG_FILENAME "config.ini"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow), mbctx(NULL), mbMasterProxy(NULL)
{
    ui->setupUi(this);

    labelPollCount.setStyleSheet("QLabel{border: 1px solid gray;}");
    labelPollCount.setAlignment(Qt::AlignVCenter | Qt::AlignHCenter);
    labelPollCount.setText(QString::asprintf(" 问询次数：%d  应答次数：%d ", 0, 0));
    ui->statusbar->addPermanentWidget(&labelPollCount);    //状态栏右下角显示永久信息

    btnClear.setText(tr("清除计数"));
    btnClear.setStyleSheet("QPushButton{border: 1px solid gray;border-radius: 5px;background-color: rgb(225, 225, 225);}"
                           "QPushButton:hover{border-color: rgb(0, 120, 215);background-color: rgb(229, 241, 251);}"
                           "QPushButton:pressed,QPushButton:checked{border-color: rgb(0, 85, 155);background-color: rgb(204, 228, 247);}");
    connect(&btnClear, &QPushButton::clicked, this, [=]{
        mbMasterProxy->clearCount();
        labelPollCount.setText(QString::asprintf(" 问询次数：%d  应答次数：%d ", mbMasterProxy->pollCount(), mbMasterProxy->respCount()));
    });
    ui->statusbar->addPermanentWidget(&btnClear);

    mbMasterProxy = new ModbusMasterProxy(NULL);
    mbMasterProxy->setProxy(&mbctx);
    connect(this, &MainWindow::mbctxMasterRead, mbMasterProxy, &ModbusMasterProxy::read);
    connect(this, &MainWindow::mbctxMasterWrite, mbMasterProxy, &ModbusMasterProxy::write);

    thdMbPoll = new QThread();
    connect(thdMbPoll, SIGNAL(finished()), thdMbPoll, SLOT(deleteLater()));

    mbMasterProxy->moveToThread(thdMbPoll);

    thdMbPoll->start();

    connect(&connectWindowRTU, &ConnectWindowRTU::initConnect, this, static_cast<void (MainWindow::*)(QString, int, int, int, int)>(&MainWindow::openPort));
    connectWindowRTU.setWindowFlag(Qt::WindowStaysOnTopHint);  // 置顶窗口
    connectWindowRTU.setWindowModality(Qt::ApplicationModal);  // 阻塞父窗口

    connect(&connectWindowTCP, &ConnectWindowTCP::initConnect, this, static_cast<void (MainWindow::*)(QString,quint16)>(&MainWindow::openPort));
    connectWindowTCP.setWindowFlag(Qt::WindowStaysOnTopHint);  // 置顶窗口
    connectWindowTCP.setWindowModality(Qt::ApplicationModal);  // 阻塞父窗口

    aboutWindow.setWindowFlag(Qt::WindowStaysOnTopHint);
    aboutWindow.setWindowModality(Qt::ApplicationModal);

    connect(ui->actionComConnect, &QAction::triggered, this, &MainWindow::connectTriggeredRTU);
    connect(ui->actionNetConnect, &QAction::triggered, this, &MainWindow::connectTriggeredTCP);
    connect(ui->actionDisconnect, &QAction::triggered, this, &MainWindow::disconnectTriggered);
    connect(ui->actionAbout, &QAction::triggered, &aboutWindow, &AboutWindow::show);
    connect(ui->actionExit, &QAction::triggered, this, &MainWindow::close);
    connect(ui->actionDataTrack, &QAction::triggered, this, &MainWindow::enableDataTrack);
    connect(ui->actionFullScreen, &QAction::triggered, this, &MainWindow::fullScreen);

    connect(&timerMbPoll, &QTimer::timeout, this, &MainWindow::timeout);

    connect(ui->btnPollCycle, static_cast<void (QPushButton::*)(bool)>(&QPushButton::clicked), this, static_cast<void (MainWindow::*)(bool)>(&MainWindow::pollCycle));
    connect(ui->btnPollSingle, static_cast<void (QPushButton::*)(bool)>(&QPushButton::clicked), this, static_cast<void (MainWindow::*)(bool)>(&MainWindow::pollSingle));

    enableUI(true);

    resize(QSettings(CONFIG_FILENAME, QSettings::IniFormat).value("windowsSize", QSize(WINDOW_DEFAULT_WIDTH, WINDOW_DEFAULT_HEIGHT)).toSize());
    QScreen *screen = qApp->primaryScreen();
    int w = screen->size().width();            // 屏幕宽
    int h = screen->size().height();           // 屏幕高
    int x = (w - WINDOW_DEFAULT_WIDTH) / 2;
    int y = (h - WINDOW_DEFAULT_HEIGHT) / 3;
    x = x < 0 ? 0 : x;
    y = y < 0 ? 0 : y;
    move(QSettings(CONFIG_FILENAME, QSettings::IniFormat).value("windowsPos", QPoint(x, y)).toPoint());

    ui->spboxPollInterval->setValue(QSettings(CONFIG_FILENAME, QSettings::IniFormat).value("pollInterval", 1000).toInt());
    ui->spboxPollTimeout->setValue(QSettings(CONFIG_FILENAME, QSettings::IniFormat).value("pollTimeout", 500).toInt());

    ui->actionDataTrack->setChecked(QSettings(CONFIG_FILENAME, QSettings::IniFormat).value("enableDataTrack", true).toBool());
    enableDataTrack(ui->actionDataTrack->isChecked());

    ui->actionFullScreen->setChecked(QSettings(CONFIG_FILENAME, QSettings::IniFormat).value("fullScreen", false).toBool());
    fullScreen(ui->actionFullScreen->isChecked());

    connect(ui->btnClearTrack, &QPushButton::clicked, ui->txtDataTrack, &QTextBrowser::clear);

    ///////////////////////////////////////////////////////////////////////////////////

    ui->cboxBaudrateSet->addItem("1200");
    ui->cboxBaudrateSet->addItem("2400");
    ui->cboxBaudrateSet->addItem("4800");
    ui->cboxBaudrateSet->addItem("9600");
    ui->cboxBaudrateSet->addItem("14400");
    ui->cboxBaudrateSet->addItem("19200");
    ui->cboxBaudrateSet->addItem("38400");
    ui->cboxBaudrateSet->addItem("56000");
    ui->cboxBaudrateSet->addItem("57600");
    ui->cboxBaudrateSet->addItem("115200");
    ui->cboxBaudrateSet->setCurrentIndex(3);

    ui->cboxDatabitsSet->addItem("8");
    ui->cboxDatabitsSet->addItem("9");

    ui->cboxStopbitsSet->addItem("1");
    ui->cboxStopbitsSet->addItem("2");

    ui->cboxParitySet->addItem(QString("无校验"));
    ui->cboxParitySet->addItem(QString("奇校验"));
    ui->cboxParitySet->addItem(QString("偶校验"));

    connect(ui->btnWriteCfg, &QPushButton::clicked, this, &MainWindow::mbMasterProxyWriteCfg);
    connect(ui->btnReadCfg, &QPushButton::clicked, this, &MainWindow::mbMasterProxyReadCfg);
    connect(ui->btnRestore, &QPushButton::clicked, this, &MainWindow::mbMasterProxyRestoreCfg);
    connect(ui->btnRestart, &QPushButton::clicked, this, &MainWindow::mbMasterProxyRestartCfg);

    ///////////////////////////////////////////////////////////////////////////////////
}

MainWindow::~MainWindow()
{
    if(thdMbPoll->isRunning())
    {
        thdMbPoll->exit(0);
        thdMbPoll->wait();
    }
    closePort();
    QSettings(CONFIG_FILENAME, QSettings::IniFormat).setValue("windowsPos", pos());
    QSettings(CONFIG_FILENAME, QSettings::IniFormat).setValue("windowsSize", size());
    QSettings(CONFIG_FILENAME, QSettings::IniFormat).setValue("pollInterval", ui->spboxPollInterval->value());
    QSettings(CONFIG_FILENAME, QSettings::IniFormat).setValue("pollTimeout", ui->spboxPollTimeout->value());
    QSettings(CONFIG_FILENAME, QSettings::IniFormat).setValue("enableDataTrack", ui->actionDataTrack->isChecked());
    QSettings(CONFIG_FILENAME, QSettings::IniFormat).setValue("fullScreen", ui->actionFullScreen->isChecked());

    delete thdMbPoll;
    delete mbMasterProxy;
    delete ui;
}

void MainWindow::modbus_recv_callback(const void *buf, uint32_t len)
{
    g_MainWnd->postEvent(ModbusRecvEvent(QByteArray((const char *)buf, len)));
}

void MainWindow::modbus_send_callback(const void *buf, uint32_t len)
{
    g_MainWnd->postEvent(ModbusSendEvent(QByteArray((const char *)buf, len)));
}

void MainWindow::postEvent(const ModbusSendEvent &event)
{
    if(ui->actionDataTrack)
        QApplication::postEvent(this, new ModbusSendEvent(event));
}

void MainWindow::postEvent(const ModbusRecvEvent &event)
{
    if(ui->actionDataTrack)
        QApplication::postEvent(this, new ModbusRecvEvent(event));
}

void MainWindow::customEvent(QEvent *e)
{
    if(e->type() == CSTM_EVENT_MODBUS_SEND)
    {
        QTime time = QDateTime::currentDateTime().time();
        QString text = QString::asprintf("[%02d:%02d:%02d:%03d]>>>", time.hour(), time.minute(), time.second(), time.msec());
        QByteArray data = static_cast<ModbusSendEvent*>(e)->data();
        foreach(char c, data)
        {
            text += QString::asprintf(" %02X", (uint8_t)c);
        }
        ui->txtDataTrack->append(text);
    }else if(e->type() == CSTM_EVENT_MODBUS_RECV)
    {
        QTime time = QDateTime::currentDateTime().time();
        QString text = QString::asprintf("[%02d:%02d:%02d:%03d]<<<", time.hour(), time.minute(), time.second(), time.msec());
        QByteArray data = static_cast<ModbusRecvEvent*>(e)->data();
        foreach(char c, data)
        {
            text += QString::asprintf(" %02X", (uint8_t)c);
        }
        ui->txtDataTrack->append(text);
    }else
    {

    }
}

void MainWindow::enableUI(bool enable)
{
    ui->actionComConnect->setEnabled(enable);
    ui->actionNetConnect->setEnabled(enable);
    ui->actionDisconnect->setEnabled(!enable);
    ui->tabWidget->setEnabled(!enable);
    ui->gboxShowCtrl->setEnabled(!enable);
}

void MainWindow::enableDataTrack(bool enable)
{
    if(enable)
    {
        ui->gboxDataTrack->show();
    }else
    {
        ui->gboxDataTrack->hide();
    }
}

void MainWindow::fullScreen(bool checked)
{
    if(checked)  // 全屏显示
    {
        this->showFullScreen();
    }else        // 退出全屏时
    {
        this->showNormal();
    }
}

void MainWindow::mbMasterProxyWrite(quint8 slaveAddress, quint8 functionCode, quint16 startAddress, quint32 numToWrite, QByteArray data, quint32 responseTimeout)
{
    if(mbMasterProxy->isConnected() && !mbMasterProxy->isBusy())
    {
        emit mbctxMasterWrite(slaveAddress, functionCode, startAddress, numToWrite, data, responseTimeout);
    }
}

void MainWindow::mbMasterProxyRead(quint8 slaveAddress, quint8 functionCode, quint16 startAddress, quint32 numToRead, quint32 responseTimeout)
{
    if(mbMasterProxy->isConnected() && !mbMasterProxy->isBusy())
    {
        emit mbctxMasterRead(slaveAddress, functionCode, startAddress, numToRead, responseTimeout);
    }
}

void MainWindow::connectTriggeredTCP(bool enable)
{
    Q_UNUSED(enable);
    connectWindowTCP.show();
}

void MainWindow::connectTriggeredRTU(bool enable)
{
    Q_UNUSED(enable);
    connectWindowRTU.show();
}

void MainWindow::disconnectTriggered(bool enable)
{
    Q_UNUSED(enable);
    closePort();
}

void MainWindow::openPort(QString portName, int baudrate, int databits, int stopbits, int parity)
{
    if(!mbctx)
    {
        QString port = "\\\\.\\" + portName;

        char c = 'N';

        if(parity == 0)
            c = 'N';
        else if(parity == 1)
            c = 'O';
        else if(parity == 2)
            c = 'E';
        else
            c = 'N';

        mbctx = modbus_new_rtu(
                    port.toStdString().c_str(),
                    baudrate,
                    c,
                    databits,
                    stopbits);

        if (modbus_connect(mbctx) < 0)
        {
            modbus_free(mbctx);
            mbctx = NULL;
            QMessageBox::critical(this, QApplication::applicationName(), QString("连接失败!\r\n请检查该端口是否存在或被占用！"), QMessageBox::Ok);
            return;
        }

        modbus_set_recv_callback(mbctx, g_MainWnd->modbus_recv_callback);
        modbus_set_send_callback(mbctx, g_MainWnd->modbus_send_callback);

        enableUI(false);

        connect(mbMasterProxy, &ModbusMasterProxy::response, this, &MainWindow::response);
    }
}

void MainWindow::openPort(QString ip, quint16 port)
{
    if(!mbctx)
    {
        mbctx = modbus_new_tcp(ip.toStdString().c_str(), port);

        if (modbus_connect(mbctx) < 0)
        {
            modbus_free(mbctx);
            mbctx = NULL;
            QMessageBox::critical(this, QApplication::applicationName(), QString("连接失败!\r\n请检查IP和端口是否正确或被占用！"), QMessageBox::Ok);
            return;
        }

        modbus_set_recv_callback(mbctx, g_MainWnd->modbus_recv_callback);
        modbus_set_send_callback(mbctx, g_MainWnd->modbus_send_callback);

        enableUI(false);

        connect(mbMasterProxy, &ModbusMasterProxy::response, this, &MainWindow::response);
    }
}

void MainWindow::closePort()
{
    if(mbctx)
    {
        disconnect(mbMasterProxy, &ModbusMasterProxy::response, this, &MainWindow::response);

        modbus_close(mbctx);
        modbus_free(mbctx);
        mbctx = NULL;
        enableUI(true);
        pollCycle(false);
    }
}

void MainWindow::timeout()
{
    mbMasterProxyRead(connectWindowRTU.slaveAddress(), 0x03, 1000, sizeof(global_var_t) >> 1, ui->spboxPollTimeout->value());
}

void MainWindow::response(quint8 slaveAddress, quint8 functionCode, quint16 startAddress, quint32 numToReadWirte, QByteArray data)
{
    Q_UNUSED(slaveAddress);
    Q_UNUSED(numToReadWirte);

    const uint8_t* buf = (const uint8_t *)data.data();
    uint32_t len = data.size();

    labelPollCount.setText(QString::asprintf(" 问询次数：%d  应答次数：%d ", mbMasterProxy->pollCount(), mbMasterProxy->respCount()));

    if(!mbMasterProxy->isConnected() &&  !mbMasterProxy->isBusy())
    {
        closePort();
        QMessageBox::critical(this, QApplication::applicationName(), tr("错误，未建立连接！"), QMessageBox::Close);
        return;
    }

    switch(functionCode)
    {
    case 0X01:
        break;
    case 0X02:
        break;
    case 0X03:
    {
        //
        // 读系统配置参数
        //
        if(startAddress == 0)
        {
            if(len == 0)  // 读失败
            {
                QMessageBox::critical(this, QApplication::applicationName(), tr("读取超时，请重试！"), QMessageBox::Close);
                break;
            }

            config_sys_t sys;

            memcpy(&sys, buf, sizeof (config_sys_t));
            QString ip      = QString::asprintf("%d.%d.%d.%d", sys.ip[0], sys.ip[1], sys.ip[2], sys.ip[3]);
            QString gw      = QString::asprintf("%d.%d.%d.%d", sys.gw[0], sys.gw[1], sys.gw[2], sys.gw[3]);
            QString netmask = QString::asprintf("%d.%d.%d.%d", sys.netmask[0], sys.netmask[1], sys.netmask[2], sys.netmask[3]);

            ui->cboxBaudrateSet->setEditText(QString::asprintf("%d", sys.baudrate));
            ui->cboxDatabitsSet->setCurrentIndex(ui->cboxDatabitsSet->findText(QString::asprintf("%d", sys.databits)));
            ui->cboxStopbitsSet->setCurrentIndex(ui->cboxStopbitsSet->findText(QString::asprintf("%d", sys.stopbits)));
            ui->cboxParitySet->setCurrentIndex(sys.parity);
            ui->spboxAddrSet->setValue(sys.address);
            ui->spboxPort->setValue(sys.port);
            ui->leditIP->setText(ip);
            ui->leditGW->setText(gw);
            ui->leditNetmask->setText(netmask);
            ui->spboxTimeout->setValue(sys.timeout);

            QMessageBox::information(this, QApplication::applicationName(), tr("读取成功！"), QMessageBox::Ok);
        }

        //
        // 读实时数据
        //
        if(startAddress == 1000)
        {
            if(len != 0)
            {
                const global_var_t* gv = (const global_var_t *)buf;
            }
        }

        break;
    }
    case 0X04:
        break;
    case 0X05:  // 写单个线圈
        break;
    case 0X06:  // 写单个寄存器
    {
        //
        // 重启和恢复出厂设置
        //
        if(startAddress == 0 || startAddress == 1)
        {
            if(len == 0)  // 失败
            {
                QMessageBox::critical(this, QApplication::applicationName(), tr("读取超时，请重试！"), QMessageBox::Close);
            }else
            {
                QMessageBox::information(this, QApplication::applicationName(), tr("执行成功！"), QMessageBox::Close);
            }
            break;
        }
        break;
    }
    case 0X0F:  // 写多个线圈
        break;
    case 0X10:  // 写多个寄存器
    {
        switch(startAddress)
        {
        case 0:
        {
            if(len == 0)  // 写失败
            {
                QMessageBox::critical(this, QApplication::applicationName(), tr("写入超时，请重试！"), QMessageBox::Close);
            }else
            {
                QMessageBox::information(this, QApplication::applicationName(), tr("写入成功！"), QMessageBox::Ok);
            }
            break;
        }
        default:break;
        }
        break;
    }

    default:break;
    }
}

void MainWindow::pollCycle(bool checked)
{
    if(checked)
    {
        timeout();
        timerMbPoll.start(ui->spboxPollInterval->value());
    }else
    {
        timerMbPoll.stop();
    }
    ui->spboxPollInterval->setEnabled(!checked);
    ui->spboxPollTimeout->setEnabled(!checked);
    ui->btnPollCycle->setChecked(checked);
}

void MainWindow::pollSingle(bool checked)
{
    Q_UNUSED(checked);
    if(mbMasterProxy->isConnected())
    {
        timeout();
    }
}

void MainWindow::mbMasterProxyReadCfg()
{
    mbMasterProxyRead(connectWindowRTU.slaveAddress(), 0X03, 0, REG_SYS_NUM, ui->spboxPollTimeout->value());
}

void MainWindow::mbMasterProxyWriteCfg()
{
    config_sys_t sys;
    memset(&sys, 0, sizeof(config_sys_t));
    sys.baudrate = ui->cboxBaudrateSet->currentText().toInt();
    sys.databits = ui->cboxDatabitsSet->currentText().toInt();
    sys.stopbits = ui->cboxStopbitsSet->currentText().toInt();
    sys.parity   = ui->cboxParitySet->currentIndex();
    sys.address  = ui->spboxAddrSet->value();
    sys.port     = ui->spboxPort->value();
    QStringList ip = ui->leditIP->text().split(".");
    QStringList gw = ui->leditGW->text().split(".");
    QStringList netmask = ui->leditNetmask->text().split(".");
    for(int i = 0; i < 4; i++)
    {
        sys.ip[i] = ip[i].toInt();
        sys.gw[i] = gw[i].toInt();
        sys.netmask[i] = netmask[i].toInt();
    }
    sys.timeout = ui->spboxTimeout->value();
    mbMasterProxyWrite(connectWindowRTU.slaveAddress(), 0X10, 0, REG_SYS_NUM, QByteArray((const char *)&sys, sizeof(config_sys_t)), ui->spboxPollTimeout->value());
}

void MainWindow::mbMasterProxyRestoreCfg()
{
    uint16_t v = 1;
    mbMasterProxyWrite(connectWindowRTU.slaveAddress(), 0X06, 1, 1, QByteArray((const char *)&v, 2), ui->spboxPollTimeout->value());
}

void MainWindow::mbMasterProxyRestartCfg()
{
    uint16_t v = 1;
    mbMasterProxyWrite(connectWindowRTU.slaveAddress(), 0X06, 0, 1, QByteArray((const char *)&v, 2), ui->spboxPollTimeout->value());
}
